package me.zhengjie.app.system.service.impl;

import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import me.zhengjie.app.system.service.AppUserService;
import me.zhengjie.app.system.service.dto.LoginUser;
import me.zhengjie.app.system.service.dto.MemberInfo;
import me.zhengjie.app.system.service.dto.UserInfo;
import me.zhengjie.modules.security.config.bean.SecurityProperties;
import me.zhengjie.modules.security.security.TokenProvider;
import me.zhengjie.modules.security.service.dto.JwtUserDto;
import me.zhengjie.modules.security.service.dto.OnlineUserDto;
import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.modules.system.service.DataService;
import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.mapstruct.UserMapper;
import me.zhengjie.utils.RedisUtils;
import me.zhengjie.utils.SpringContextHolder;
import me.zhengjie.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Service
public class AppUserServiceImpl implements AppUserService {


    @Value("${rsa.private_key}")
    private String privateKey;

    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final UserDetailsService userDetailsService;
    private final PhoneOnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    @Resource
    private UserService userService;

    @Resource
    private UserMapper userMapper;

//    @Resource
//    private SmsService smsService;

    @Resource
    private RoleService roleService;
    @Resource
    private DataService dataService;

    @Resource
    private UserRepository userRepository;

    public AppUserServiceImpl(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, PhoneOnlineUserService onlineUserService, TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.properties = properties;
        this.redisUtils = redisUtils;
        this.userDetailsService = userDetailsService;
        this.onlineUserService = onlineUserService;
        this.tokenProvider = tokenProvider;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    /**
     * @param appUser
     * @param request
     * @return
     */
    public Map<String, Object> login(LoginUser appUser, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        Map<String, Object> authInfo;
        try {
            RSA rsa = new RSA(privateKey, null);
            String password = new String(rsa.decrypt(appUser.getPassword(), KeyType.PrivateKey));
            User user = userService.findByPhone(appUser.getMobile());
            if (user == null || StringUtils.isBlank(user.getUsername())) {
                result.put("code", 400);
                result.put("message", "用户不存在");
                return result;
            }
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), password);

            Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

            SecurityContextHolder.getContext().setAuthentication(authentication);
            // 生成令牌
            String token = tokenProvider.createToken(authentication);

            final JwtUserDto jwtUser = (JwtUserDto) authentication.getPrincipal();

            // 保存在线信息

            onlineUserService.save(jwtUser, token, request);

//            UserInfo memberInfo = new UserInfo(jwtUser);

            // 返回 token 与 用户信息
            authInfo = new HashMap<String, Object>(2) {{
                put("access_token", properties.getTokenStartWith() + properties.getPhoneAccessToken() + token);
                put("refresh_token", properties.getTokenStartWith() + properties.getPhoneRefreshToken() + token);
                put("member", jwtUser.getUser());
            }};

//            user.setClientid(appUser.getClientid());
            userRepository.save(user);
        } catch (AuthenticationException e) {
            result.put("code", 500);
            result.put("message", "登录失败,请联系管理员!");
            return result;
        }
        result.put("code", 200);
        result.put("message", "登录成功");
        result.put("data", authInfo);
        return result;
    }

    /**
     * @param aappUser
     * @param request
     * @return
     */
    public Map<String, Object> logout(LoginUser aappUser, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        try {
            onlineUserService.logout(tokenProvider.getToken(request));
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "退出失败,请联系管理员!");
            return result;
        }
        result.put("code", 200);
        result.put("message", "退出成功");
        return result;
    }

    public Map<String, Object> getUserInfo(HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        OnlineUserDto onlineUser = null;
        try {
            String token = resolveToken(request);
            if (StringUtils.isNotEmpty(token)) {
                onlineUser = onlineUserService.getOne(token);
            }
            if (onlineUser != null) {
                MemberInfo memberInfo = new MemberInfo(onlineUser);
                User user = userService.findByPhone(memberInfo.getMobile());
                if (user.getRoles() != null && user.getRoles().size() > 0) {
                    String roleName = "";
                    for (Role role : user.getRoles()) {
                        if (StringUtils.isNotEmpty(role.getName())) {
                            roleName += role.getName();
                        }
                    }
                    memberInfo.setRole(roleName);
                }
                if (StringUtils.isNotEmpty(user.getGender())) {
                    if ("男".equals(user.getGender())) {
                        memberInfo.setGender("1");
                    } else if ("女".equals(user.getGender())) {
                        memberInfo.setGender("2");
                    } else {
                        memberInfo.setGender("0");
                    }
                }
                if (StringUtils.isNotEmpty(user.getEmail())) {
                    memberInfo.setEmail(user.getEmail());
                }
                if (StringUtils.isNotEmpty(user.getAvatarName())) {
                    memberInfo.setAvatarPath(user.getAvatarName());
                }

                result.put("data", memberInfo);
            }
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "ERROR");
            return result;
        }
        result.put("code", 200);
        result.put("message", "OK");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }

    private String resolveToken(HttpServletRequest request) {
        SecurityProperties properties = SpringContextHolder.getBean(SecurityProperties.class);
        String bearerToken = request.getHeader(properties.getHeader());
        if (org.springframework.util.StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) {
            return bearerToken.substring(7);
        }
        return null;
    }

    public Map<String, Object> smsCode(String mobile) {
        String verifyCode;
        Map<String, Object> result = null;
        boolean sendFlag = true;
        if (redisUtils.hasKey("VerificationCode" + mobile)) {
            long expireTime = redisUtils.getExpire("VerificationCode" + mobile);
            if (expireTime > 60) {
                sendFlag = false;
                result = new HashMap<>();
                result.put("code", 400);
                result.put("message", "验证码还在有效期！");
            } else {
                redisUtils.del("VerificationCode" + mobile);
            }
        }
//        if (sendFlag) {
//            verifyCode = (new Random().nextInt(899999) + 100000) + "";//生成短信验证码
//            result = smsService.sendVerificationCode(mobile, verifyCode);
//            if ("200".equals(result.get("code").toString())) {
//                redisUtils.set("VerificationCode" + mobile, verifyCode, 60 * 5);
//                result.put("data", verifyCode);
//            }
//
//        }
        return result;

    }

    @Override
    public Map<String, Object> mobileLogin(LoginUser appUser, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        Map<String, Object> authInfo;
        try {
            User user = userService.findByPhone(appUser.getMobile());
            if (user == null || StringUtils.isBlank(user.getUsername())) {
                result.put("code", 400);
                result.put("message", "用户不存在");
                return result;
            }
            if (redisUtils.hasKey("VerificationCode" + appUser.getMobile())) {
                long expireTime = redisUtils.getExpire("VerificationCode" + appUser.getMobile());
                if (expireTime < 10) {
                    result = new HashMap<>();
                    result.put("code", 400);
                    result.put("message", "验证码已失效！");
                    redisUtils.del("VerificationCode" + appUser.getMobile());
                    return result;
                }
                String code = redisUtils.get("VerificationCode" + appUser.getMobile()).toString();
                if (!code.equals(appUser.getCode())) {
                    result.put("code", 400);
                    result.put("message", "验证码不正确！");
                    return result;
                }
            } else {
                result = new HashMap<>();
                result.put("code", 400);
                result.put("message", "验证码不存在！");
                return result;
            }
            // 生成令牌
            String token = tokenProvider.createToken(user.getUsername());

            final JwtUserDto jwtUser= new JwtUserDto(
                    userMapper.toDto(user),
                    dataService.getDeptIds(userMapper.toDto(user)),
                    roleService.mapToGrantedAuthorities(userMapper.toDto(user))
            );
            // 保存在线信息
            onlineUserService.save(jwtUser, token, request);

            UserInfo memberInfo = new UserInfo(jwtUser);
            // 返回 token 与 用户信息
            authInfo = new HashMap<String, Object>(2) {{
                put("access_token", properties.getTokenStartWith() + properties.getPhoneAccessToken() + token);
                put("refresh_token", properties.getTokenStartWith() + properties.getPhoneRefreshToken() + token);
                put("member", memberInfo);
            }};
//            user.setClientid(appUser.getClientid());
            userService.update(user);
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "登录失败,请联系管理员!");
            e.printStackTrace();
            return result;
        }
        result.put("code", 200);
        result.put("message", "登录成功");
        result.put("data", authInfo);
        return result;
    }
}
